TooltipScan = AceLibrary("AceAddon-2.0"):new("AceConsole-2.0", "AceEvent-2.0", "AceDB-2.0", "AceHook-2.1", "AceDebug-2.0")
TooltipScan:RegisterDB("BonusesSV", "BonusesSVPC")
TooltipScan:RegisterDefaults("profile", {
	["IgnoreStats"] = true,
	["IgnoreArmor"] = 0,
	["debug"] = false,
	["overridestat"] = {}
} )

function TooltipScan:OnInitialize()
	self.options = {
		frame = nil,
		boxes = {},
		titles = {},
	}
	self.INV_SLOT = {
		["INVTYPE_AMMO"]={0},
		["INVTYPE_HEAD"]={1},
		["INVTYPE_NECK"]={2},
		["INVTYPE_SHOULDER"]={3},
		["INVTYPE_BODY"]={4},
		["INVTYPE_CHEST"]={5},
		["INVTYPE_ROBE"]={5},
		["INVTYPE_WAIST"]={6},
		["INVTYPE_LEGS"]={7},
		["INVTYPE_FEET"]={8},
		["INVTYPE_WRIST"]={9},
		["INVTYPE_HAND"]={10},
		["INVTYPE_FINGER"]={11,12},
		["INVTYPE_TRINKET"]={13,14},
		["INVTYPE_CLOAK"]={15},
		["INVTYPE_WEAPON"]={16,17},
		["INVTYPE_SHIELD"]={17},
		["INVTYPE_2HWEAPON"]={16,17},
		["INVTYPE_WEAPONMAINHAND"]={16},
		["INVTYPE_WEAPONOFFHAND"]={17},
		["INVTYPE_HOLDABLE"]={17},
		["INVTYPE_RANGED"]={18},
		["INVTYPE_THROWN"]={18},
		["INVTYPE_RANGEDRIGHT"]={18},
		["INVTYPE_RELIC"]={18},
		["INVTYPE_TABARD"]={19},
	}
	
	self.ClassIgnoreList = {
		-------------------------------------------------------
		-- List of which stats to ignore for each class		 --
		-- If a class is listed under a stat, it			 --
		-- means that its an unnecessary stat for that class --
		-------------------------------------------------------
		--Base Stats
		["STR"] = {"MAGE","PRIEST","WARLOCK"},
		["STA"] = {},
		["INT"] = {"ROGUE","WARRIOR",},
		["SPI"] = {"ROGUE","WARRIOR",},
		["AGI"] = {"MAGE","PALADIN","PRIEST","WARLOCK",},
		["ARMOR"] = {"MAGE","PRIEST","WARLOCK",},
		--General
		["DMG"] = {"HUNTER","ROGUE","WARRIOR",},
		["HEAL"] = {"HUNTER","MAGE","ROGUE","WARLOCK","WARRIOR",},
		["MP5"] = {"ROGUE","WARRIOR",},
		["AP"] = {"MAGE","PRIEST","WARLOCK",},
		["FERAL_AP"] = {"HUNTER","MAGE","PALADIN","PRIEST","ROGUE","SHAMAN","WARLOCK","WARRIOR",},
		--Combat Ratings
		["CR_DEFENSE"] = {"HUNTER","MAGE","PRIEST","ROGUE","SHAMAN","WARLOCK",},
		["CR_DODGE"] = {"MAGE","PRIEST","WARLOCK",},
		["CR_BLOCK"] = {"DRUID","HUNTER","MAGE","PRIEST","ROGUE","WARLOCK",},
		["CR_PARRY"] = {"DRUID","MAGE","PRIEST","WARLOCK",},
		["BLOCK_VALUE"] = {"DRUID","HUNTER","MAGE","PRIEST","ROGUE","WARLOCK",},
		["CR_SPELL_CRIT"] = {"HUNTER","ROGUE","WARRIOR",},
		["CR_SPELL_HIT"] = {"HUNTER","ROGUE","WARRIOR",},
		["CR_SPELL_HASTE"] = {"HUNTER","ROGUE","WARRIOR",},
		["CR_MELEE_CRIT"] = {"MAGE","PRIEST","WARLOCK",},
		["CR_MELEE_HIT"] = {"MAGE","PRIEST","WARLOCK",},
		["CR_MELEE_HASTE"] = {"MAGE","PRIEST","WARLOCK",},
		["CR_WEAPON_SKILL"] = {"DRUID","HUNTER","MAGE","PRIEST","SHAMAN","WARLOCK",},
		["RESILIENCE"] = {},
		--Resists
		["FIRE_RESIST"] = {},
		["FROST_RESIST"] = {},
		["NATURE_RESIST"] = {},
		["ARCANE_RESIST"] = {},
		["SHADOW_RESIST"] = {},
		--Specific Damage
		["FIRE_DMG"] = {"DRUID","HUNTER","PALADIN","PRIEST","ROGUE","WARRIOR",},
		["FROST_DMG"] = {"DRUID","HUNTER","PALADIN","PRIEST","ROGUE","WARLOCK","WARRIOR",},
		["NATURE_DMG"] = {"HUNTER","MAGE","PALADIN","PRIEST","ROGUE","WARLOCK","WARRIOR",},
		["ARCANE_DMG"] = {"HUNTER","PALADIN","PRIEST","ROGUE","SHAMAN","WARLOCK","WARRIOR",},
		["SHADOW_DMG"] = {"DRUID","HUNTER","MAGE","PALADIN","ROGUE","SHAMAN","WARRIOR",},
		
		["SPELL_PENETRATION"] = {"HUNTER", "ROGUE", "WARRIOR",},
	}
	
	self.NAMES = {
		STR = "Strength",
		STA = "Stamina",
		INT = "Intellect",
		SPI = "Spirit",
		AGI = "Agility",
		ARMOR = "Armor",
		
		DMG = "+Damage",
		HEAL = "+Healing",
		MP5 = "Mana per 5",
		AP = "Attack Power",
		FERAL_AP = "Feral Attack Power",
		CR_DEFENSE = "Defense Rating",
		CR_DODGE = "Dodge Rating",
		CR_BLOCK = "Block Rating",
		CR_PARRY = "Parry Rating",
		BLOCK_VALUE = "Block Value",
		CR_SPELL_CRIT = "Spell Crit Rating",
		CR_SPELL_HIT = "Spell Hit Rating",
		CR_SPELL_HASTE = "Spell Haste Rating",
		CR_MELEE_CRIT = "Crit Rating",
		CR_MELEE_HIT = "Hit Rating",
		CR_MELEE_HASTE = "Haste Rating",
		CR_WEAPON_SKILL = "Weapon Skill",
		RESILIENCE = "Resilience",
		
		FIRE_RESIST = "Fire Resistance",
		FROST_RESIST = "Frost Resistance",
		NATURE_RESIST = "Nature Resistance",
		ARCANE_RESIST = "Arcane Resistance",
		SHADOW_RESIST = "Shadow Resistance",
		
		FIRE_DMG = "Fire Damage",
		FROST_DMG = "Frost Damage",
		NATURE_DMG = "Nature Damage",
		ARCANE_DMG = "Arcane Damage",
		SHADOW_DMG = "Shadow Damage",
		
		SPELL_PENETRATION = "Spell Penetration",
	}
	
	self.numberPatterns = {
		{pattern = "^(%d+) armor", separators = {},},
		{pattern = "^(%d+) block", separators = {},},
		{pattern = " by (%d+)", separators = {" and "},},
		{pattern = " by up to (%d+)", separators = {" and "},},
		{pattern = "%+(%d+)", separators = {"/", " and ", ","},},
		{pattern = "(%d+) mana .+ 5 sec", separators = {},},
	}
	
	self.ratingNames = {
		-------------------------------
		--Used to find which stat to apply the amount of bonus to.
		--First field is the string to search for
		--Second field is an array of which stats to apply the bonus to
		--Third (Optional) field is if the amound needs to be changed in any way before added to the stat (EG. +Mana -> +Int)
		-------------------------------
		
		--Base Stats
		{"strength", {"STR"}},
		{"stamina", {"STA"}},
		{"intellect", {"INT"}},
		{"agility", {"AGI"}},
		{"spirit", {"SPI"}},
		{"armor", {"ARMOR"}},
		--Spell stats
		{"fire damage", {"FIRE_DMG"}},
		{"frost damage", {"FROST_DMG"}},
		{"nature damage", {"NATURE_DMG"}},
		{"arcane damage", {"ARCANE_DMG"}},
		{"shadow damage", {"SHADOW_DMG"}},
		{"fire spell damage", {"FIRE_DMG"}},
		{"frost spell damage", {"FROST_DMG"}},
		{"nature spell damage", {"NATURE_DMG"}},
		{"arcane spell damage", {"ARCANE_DMG"}},
		{"shadow spell damage", {"SHADOW_DMG"}},
		{"fire spells", {"FIRE_DMG"}},
		{"frost spells", {"FROST_DMG"}},
		{"nature spells", {"NATURE_DMG"}},
		{"arcane spells", {"ARCANE_DMG"}},
		{"shadow spells", {"SHADOW_DMG"}},
		
		{"healing", {"HEAL"}},
		{"damage", {"DMG"}},
		{"mana .+ 5 sec", {"MP5"}},
		--Combat Ratings
		{"defense rating", {"CR_DEFENSE"}},
		{"dodge rating", {"CR_DODGE"}},
		{"block rating", {"CR_BLOCK"}}, -- block enchant: "+10 Shield Block Rating"
		{"parry rating", {"CR_PARRY"}},
		{"block value", {"BLOCK_VALUE"}},
		{"block", {"BLOCK_VALUE"}},
	
		{"spell critical strike rating", {"CR_SPELL_CRIT"}},
		{"spell critical hit rating", {"CR_SPELL_CRIT"}},
		{"spell critical rating", {"CR_SPELL_CRIT"}},
		{"spell crit rating", {"CR_SPELL_CRIT"}},
		
		{"critical strike rating", {"CR_MELEE_CRIT"}},
		{"critical hit rating", {"CR_MELEE_CRIT"}},
		{"crit rating", {"CR_MELEE_CRIT"}},
		
		{"spell hit rating", {"CR_SPELL_HIT"}},
		{"hit rating", {"CR_MELEE_HIT"}},
		
		{"resilience", {"RESILIENCE"}}, -- resilience is implicitly a rating
		{"spell penetration", {"SPELL_PENETRATION"}},
		
		{"spell haste rating", {"CR_SPELL_HASTE"}},
		{"haste rating", {"CR_MELEE_HASTE"}},
		{"speed rating", {"CR_MELEE_HASTE"}}, -- [Drums of Battle]
		
		{"skill rating", {"CR_WEAPON_SKILL"}},
		{"cat", {"FERAL_AP"}},
		{"attack power", {"AP"}},		
		
		{"fire resistance", {"FIRE_RESIST"}},
		{"frost resistance", {"FROST_RESIST"}},
		{"nature resistance", {"NATURE_RESIST"}},
		{"arcane resistance", {"ARCANE_RESIST"}},
		{"shadow resistance", {"SHADOW_RESIST"}},
		
		{"mana", {"INT"}, function(a) return a/10 end},
		{"health", {"STA"}, function(a) return a/10 end},
		{"all stats", {"STR","STA","AGI","SPI","INT"}},
	}
	
	self.ArmorTable = {
		["DRUID"] = {"Cloth", "Leather"},
		["HUNTER"] = {"Cloth", "Leather", "Mail"},
		["MAGE"] = {"Cloth"},
		["PALADIN"] = {"Cloth", "Leather", "Mail", "Plate"},
		["PRIEST"] = {"Cloth"},
		["ROGUE"] = {"Cloth", "Leather"},
		["SHAMAN"] = {"Cloth", "Leather", "Mail"},
		["WARLOCK"] = {"Cloth"},
		["WARRIOR"] = {"Cloth", "Leather", "Mail", "Plate"},
	}
	
	self:SetupFrame()
	self:SetDebugging(self.db.profile.debug)
	self.options = {
		type="group",
		args = {
			ignorestats = {
				name = "Ignore Stats", type = 'toggle',
				desc = "Remove useless stats from the tooltip",
				get = 	function()
							return self.db.profile.IgnoreStats
						end,
				set = 	function(v)
							self.db.profile.IgnoreStats = v
						end,
			},
			ignorearmor = {
				name = "Ignore Armor", 
				type = "group",
				desc = "Hide the frame when you cant use the armor",
				args = {
					hidetooltip = {
						name = "Hide Tooltip", type = 'toggle', isRadio = true,
						desc = "Hide the whole tooltip",
						get = 	function()
									return self.db.profile.IgnoreArmor == 2
								end,
						set = 	function(v)
									self.db.profile.IgnoreArmor = 2
								end,
					},
					hidecompare = {
						name = "Hide Comparison", type = 'toggle', isRadio = true,
						desc = "Only hide the stat comparison",
						get = 	function()
									return self.db.profile.IgnoreArmor == 1
								end,
						set = 	function(v)
									self.db.profile.IgnoreArmor = 1
								end,
					},
					hidenone = {
						name = "Hide Nothing", type = 'toggle', isRadio = true,
						desc = "Dont hide anything",
						get = 	function()
									return self.db.profile.IgnoreArmor == 0
								end,
						set = 	function(v)
									self.db.profile.IgnoreArmor = 0
								end,
					},
				},
			},
			debug = {
				name = "Debugging", type = 'toggle',
				desc = "Don't turn this on unless you want alot of spam.",
				get = 	function()
							return self.db.profile.debug
						end,
				set = 	function(v)
							self.db.profile.debug = v
							self:SetDebugging(self.db.profile.debug)
						end,
			},
			options = {
				name = "Show Option Frame", type = 'execute',
				desc = "Options!",
				func = function()
					self:CreateFrame()
				end,
			},
		},
	}
	self:RegisterChatCommand({"/bonuses"}, self.options)
end

function TooltipScan:SetupFrame()
	self.frame1 = CreateFrame("GameTooltip", "TooltipScan1", GameTooltip, "GameTooltipTemplate")
	self.frame2 = CreateFrame("GameTooltip", "TooltipScan2", TooltipScan1, "GameTooltipTemplate")
	self.frame3 = CreateFrame("GameTooltip", "TooltipScan3", ItemRefTooltip, "GameTooltipTemplate")
	self.frame4 = CreateFrame("GameTooltip", "TooltipScan4", TooltipScan3, "GameTooltipTemplate")
	
	for i=1, 4 do
		t = getglobal("TooltipScan"..i)
		L1 = getglobal("TooltipScan"..i .. "TextLeft1")
		local ce = t:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
		ce:SetText("|cff808080".. CURRENTLY_EQUIPPED)

		ce:SetPoint("TOPLEFT", t, "TOPLEFT", 10, -10)
		L1:SetPoint("TOPLEFT", ce, "BOTTOMLEFT", 0, -2)
	end
end

function TooltipScan:OnEnable()
	self:HookScript(GameTooltip, "OnTooltipSetItem")
	self:HookScript(ItemRefTooltip, "OnTooltipSetItem")
	self:HookScript(ShoppingTooltip1, "OnShow")
	self:HookScript(ShoppingTooltip2, "OnShow")
	self:Debug("Registered")
end

function TooltipScan:OnShow(...)
	ShoppingTooltip1:Hide()
	ShoppingTooltip2:Hide()
end

function TooltipScan:OnTooltipSetItem(frame, ...)
	local f = GetMouseFocus() and GetMouseFocus():GetName() or ""
	if f == "WorldFrame" or string.find(f, "^Character.*Slot$") or string.find(f, "^TempEnchant%d+$") then return end
	if self:ArmorIgnore(frame) then return end
	self:Process(frame)
	return self.hooks[frame].OnTooltipSetItem(frame, ...)
end

--Comparison Functions
function TooltipScan:Process(tooltip)
	local _,link = tooltip:GetItem()
	self:Debug(link)
	local _,_,_,_,_,_,_,_,loc = GetItemInfo(link)
	self:Debug("Loc", loc)
	
	local frame1 = self.frame1
	local frame2 = self.frame2
	
	if(tooltip == ItemRefTooltip) then
		frame1 = self.frame3
		frame2 = self.frame4
	end	
	
	local anchor1, anchor2, tipmid = "TOPLEFT", "TOPRIGHT", ((tooltip and tooltip:GetLeft() or 0)+(tooltip and tooltip:GetRight() or 0))/2
	if tipmid and (tipmid * tooltip:GetScale()) >= (UIParent:GetRight()/2) then anchor1, anchor2 = anchor2, anchor1 end
	
	local TTbonuses = self:Scan(tooltip)
	
	for _, v in ipairs(self.INV_SLOT[loc] or {}) do	
		local Changes = {}
		frame1:SetOwner(GameTooltip, "ANCHOR_NONE")
		frame1:ClearAllPoints()
		frame1:SetPoint(anchor1, frame1:GetParent(), anchor2, 0, 0)
		local show = frame1:SetInventoryItem("player", v)
		if not (self.db.profile.IgnoreArmor == 1) then
			------------------------------
			local EQbonuses = self:Scan(frame1)
			for stat, amount in pairs(EQbonuses) do
				Changes[stat] = (TTbonuses[stat] or 0) - amount				
			end
			for stat, amount in pairs(TTbonuses) do
				if (EQbonuses[stat] == nil) then
					Changes[stat] = amount
				end
			end
			------------------------------
			for k,v in pairs(Changes) do
				frame1:AddLine("----------",1,1,1)
				for stat,amount in self:Sorted(Changes, function(a,b) return Changes[a] > Changes[b] end) do
					self:FormatAdd(frame1, stat, amount)
				end
				break
			end
		end
		frame1:AddLine(" ")
		if show then frame1:Show() end
		frame1 = frame2
	end
end

function TooltipScan:Scan(tooltip)
	local Cbonuses = {}
	for i = 1, tooltip:NumLines() do
		local fontString = getglobal(tooltip:GetName().."TextLeft"..i)
		local lineText = fontString:GetText()
		local r,g,b = fontString:GetTextColor()
		local color =  string.format("|cff%02x%02x%02x", (r*255), (g*255), (b*255));
		self:Debug("Line ", i , lineText)
		if not(color == "|cff7f7f7f") then
			if not(strfind(lineText, "^Use:")) then
				if lineText and strfind(lineText, "%d") then
					self:Debug("Found Number on line ", i)
					for _, p in ipairs(self.numberPatterns) do
						local text = lineText
						local lowerText = string.lower(text) -- convert to lower so we don't have to worry about same ratings with different cases
						-- Capture the increased amount
						local s, e, amount = strfind(lowerText, p.pattern)
						--self:Debug(lowerText, p.pattern, amount)
						if (amount) then
							self:Debug("Amount found: ", amount, " Pattern: ", p.pattern)
							for _, s in ipairs(p.separators) do
								if strfind(lineText, s) then
									separator = s
									-- Replace separator strings found in lineText with a single character "/", so we can use strsplit()
									lowerText = string.gsub(lowerText, s, "/")
									-- only need support for 1 kind of separator per line, we will use the first one found, so break out here
									self:Debug("seperator found: ", s)
									break
								end
							end
							lowerText = lowerText .. "/!!!"
							local textSplit = {}
							while not(lowerText == "!!!") do
								--self:Debug(lowerText)
								local place = string.find(lowerText, "/")
								table.insert(textSplit, string.sub(lowerText, 1, place-1))
								lowerText = string.sub(lowerText, place+1)	
							end
							for _, lowerText in ipairs(textSplit) do	--BUG: 2 values in one line:  2nd value isn't being checked. 1st is being used.  
								self:Debug("textSplit: ", lowerText)
								local ratingID
								local _,_,newvalue = strfind(lowerText, "(%d+)")
								if newvalue then amount = newvalue end
								for _, rating in ipairs(self.ratingNames) do
									--self:Debug(rating[1], lowerText)
									if strfind(lowerText, rating[1]) then
										self:Debug("Found ratingID ", rating[1], " for ", lowerText)
										for _,stat in ipairs(rating[2]) do
											if rating[3] then
												self:AddStat(Cbonuses, stat, rating[3](amount))
											else
												self:AddStat(Cbonuses, stat, amount)
											end
										end
										break
									end
								end
							end
						end
					end
				end
			else
				self:Debug("On Activate")
			end
		else
			self:Debug("Bad Color")
		end
	end
	return Cbonuses
end

function TooltipScan:AddStat(table, stat, value)
	table[stat] = (table[stat] or 0) + tonumber(value)
	self:Debug("Stat Added",self.NAMES[stat], value, table[stat])
end

function TooltipScan:FormatAdd(frame, s, a)
	if not(a == 0) and not(self:ClassIgnore(s)) then
		if(a > 0) then	
			a = "|cFF00FF00+" .. a .. "|r"
		elseif (a < 0) then
			a = "|cFFFF0000" .. a .. "|r"
		end
		frame:AddDoubleLine(self.NAMES[s], a, 1,1,1)
	end
end

function TooltipScan:Sorted(t, f)
	local a = {}
	for n in pairs(t) do table.insert(a, n) end
	table.sort(a, f)
	local i = 0      -- iterator variable
	local iter = function ()   -- iterator function
		i = i + 1
		if a[i] == nil then return nil
		else return a[i], t[a[i]]
		end
	end
	return iter
end

function TooltipScan:ClassIgnore(stat)
	-- True: Ignore the stat
	-- False: Add the stat in
	self:Debug(stat, self.db.profile.overridestat[stat])
	if not(self.db.profile.overridestat[stat] == nil) then
		return self.db.profile.overridestat[stat]
	end
	if (self.db.profile.IgnoreStats == true) then
		local _,class = UnitClass("player")
		if not(self.ClassIgnoreList[stat]) then return false end	--False: stat not found in table
		for _,v in pairs(self.ClassIgnoreList[stat]) do
			if(v == class) then return true end			--True: stat was found for the players class
		end	
	end
	return false										--False: option is off, not ignoring any stats
end

function TooltipScan:ArmorIgnore(tooltip)	
	--True: Hide the tooltip
	--False: Show the tooltip
	if (self.db.profile.IgnoreArmor == 2) then
		local _,class = UnitClass("player")
		for i=1, tooltip:NumLines() do
			local r,g,b = getglobal(tooltip:GetName() .."TextRight"..i):GetTextColor()
			local text = getglobal(tooltip:GetName() .."TextRight"..i):GetText()
			if text then
				local color =  string.format("|cff%02x%02x%02x", (r*255), (g*255), (b*255));
				if (color == "|cfffe1f1f") then return true end	--True: Armor not usable, hide it
				break
			end
		end	
		return false									--False: Armor is usable
	end
	return false						--False: Option is off, show by default
end
